﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RATSharp.Model;
using RATSharp.Rest;
using RATSharp.Collection;
using RATSharp;
using System.Threading.Tasks;
using RATSharp.Validator;

namespace RATSharp.Cal
{
    delegate void InvalidateCalendarDelegate();

    class PersonalReservationHandler : IReservationHandler
    {

        RestService service;
        InvalidateCalendarDelegate invalidate;
        public PersonalReservationHandler(RestService service, InvalidateCalendarDelegate invalidateCalendar)
        {
            this.service = service;
            this.invalidate = invalidateCalendar;
        }


        public void handle(Period period)
        {
            bool stepback = false;
            while (!stepback)
            {
                MenuList<MenuItem<Period, CalendarPeriodHandler>, MenuItem<string, CalendarPeriodHandler>> periodList =
                  new MenuList<MenuItem<Period, CalendarPeriodHandler>, MenuItem<string, CalendarPeriodHandler>>("Reservation Period from " + String.Format("{0:dd.MM yyyy}", period.fromDate));

                periodList.Add(new MenuItem<Period, CalendarPeriodHandler>(period, new CalendarPeriodHandler(handle)));
                periodList.AddAdditional(new MenuItem<string, CalendarPeriodHandler>("Delete reservation", new CalendarPeriodHandler(deleteReservation)));

                if (period.reservation.periods.Count > 1)
                {
                    periodList.AddAdditional(new MenuItem<string, CalendarPeriodHandler>("See all periods in this reservation", new CalendarPeriodHandler(seeAllPeriods)));
                }

                periodList.AddAdditional(new MenuItem<string, CalendarPeriodHandler>("Edit reservation", new CalendarPeriodHandler(editReservation)));
                periodList.AddAdditional(new MenuItem<string, CalendarPeriodHandler>("Step Back", delegate(Period delPer) { stepback = true; }));

                Console.WriteLine(periodList);
                int choice = MenuUtilities.GetSimpleInput(new IntRangeInputValidator(1, periodList.fullCount)) - 1;

                if (choice < periodList.Count)
                {
                    periodList.ElementAt(choice).menuHandler(period);
                }
                else
                {
                    periodList.GetAdditional(choice).menuHandler(period);
                }

            }
        }

        private void editReservation(Period period)
        {
            //TODO:
            //not specified how to do that
            Console.WriteLine("Not implemented yet");
        }

        private void seeAllPeriods(Period period)
        {
            bool stepback = false;
            while (!stepback)
            {
                MenuList<MenuItem<Period, CalendarPeriodHandler>, MenuItem<string, CalendarPeriodHandler>> periodList =
                  new MenuList<MenuItem<Period, CalendarPeriodHandler>, MenuItem<string, CalendarPeriodHandler>>("Reservation Periods");
                foreach (Period per in period.reservation.periods)
                {
                    periodList.Add(new MenuItem<Period, CalendarPeriodHandler>(per, new CalendarPeriodHandler(modifyPeriod)));
                }
                periodList.AddAdditional(new MenuItem<string, CalendarPeriodHandler>("Step Back", delegate(Period delPer) { stepback = true; }));


                Console.Write(periodList);
                Console.WriteLine("Choosing of period will switch periods valid state");

                int choice = MenuUtilities.GetSimpleInput(new IntRangeInputValidator(1, periodList.fullCount)) - 1;

                if (choice < periodList.Count)
                {
                    periodList.ElementAt(choice).menuHandler(periodList.ElementAt(choice).value);
                }
                else
                {
                    periodList.GetAdditional(choice).menuHandler(null);
                }

            }
        }

        private void modifyPeriod(Period period)
        {
            period.valid = !period.valid;

            Period responsePeriod;
            if (!MenuUtilities.WaitForCommTask(service.modifyPeriod(period), out responsePeriod))
            {
                period.valid = !period.valid;
                return;
            }
        }

        private void deleteReservation(Period period)
        {
            Console.WriteLine("Are you sure? (yes/no)");
            MenuUtilities.YES_NO_CHOICE choice = MenuUtilities.GetSimpleInput(new StringInputEnumValidator<MenuUtilities.YES_NO_CHOICE>());
            if (choice == MenuUtilities.YES_NO_CHOICE.no)
            {
                return;
            }
            else
            {
                Reservations res;
                if (!MenuUtilities.WaitForCommTask(service.deleteReservation(period.reservation), out res))
                {
                    return;
                }

                invalidate();
                return;
            }
        }

        public MenuItem<string, CalendarDateTimeHandler> getAllowedCalendarActions()
        {
            return new MenuItem<string, CalendarDateTimeHandler>("Create New Reservation", new CalendarDateTimeHandler(createNewReservation));
        }

        private void createNewReservation(DateTime dateTime)
        {
            if (DateTime.Compare(dateTime, DateTime.Now.Date) < 0)
            {
                Console.WriteLine("You cant make reservation in the past!");
                return;
            }

            ObjectBrowser browser = new ObjectBrowser(service);
            RATSharp.Model.Object obj = browser.init();
            if (obj == null)
            {
                return;
            }


            while (true)
            {
                Reservations responseRes;
                Task<Reservations> task = addReservation(dateTime, obj);
                if (task == null)
                {
                    return;
                }

                if (!MenuUtilities.WaitForCommTask(task, out responseRes))
                {
                    return;
                }

                if (responseRes.Count != 0)
                {
                    Console.WriteLine("Following reservation is already created in your term!");
                    invalidate();
                    Console.WriteLine(responseRes.First());
                    Console.WriteLine("Retry? (yes/no)");
                    MenuUtilities.YES_NO_CHOICE choiceRetry = MenuUtilities.GetSimpleInput(new StringInputEnumValidator<MenuUtilities.YES_NO_CHOICE>());
                    if (choiceRetry != MenuUtilities.YES_NO_CHOICE.yes)
                    {
                        return;
                    }

                }
                else
                {
                    Console.WriteLine("Reservation succesfully created!");
                    invalidate();
                    return;
                }
            }

        }

        private Task<Reservations> addReservation(DateTime dateTime, RATSharp.Model.Object obj)
        {
            //TODO: it should be possible to make a reservation over a midnight (not implemented as we want to keep console app as simple as possible)
            DateTime since;
            DateTime to;
            do
            {
                Console.WriteLine("Since? (HH:mm)");
                since = MenuUtilities.GetSimpleInput(new StringInputTimeValidator("HH:mm"));
                Console.WriteLine("To? (HH:mm)");
                to = MenuUtilities.GetSimpleInput(new StringInputTimeValidator("HH:mm"));
            } while (!(DateTime.Compare(to, since) > 0));

            //merge date and time
            since = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, since.Hour, since.Minute, 0);
            to = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, to.Hour, to.Minute, 0);

            if (DateTime.Compare(since, DateTime.Now) < 0 || DateTime.Compare(to, DateTime.Now) < 0)
            {
                Console.WriteLine("Its not possible to make reservations in the past");
                return null;
            }

            Console.WriteLine("Periodical? (yes/no)");
            MenuUtilities.YES_NO_CHOICE choice = MenuUtilities.GetSimpleInput(new StringInputEnumValidator<MenuUtilities.YES_NO_CHOICE>());
            int numberOfWeeks;
            if (choice == MenuUtilities.YES_NO_CHOICE.yes)
            {
                Console.WriteLine("How Many Weeks?");
                //TODO: Magic constants, should be configurable on server!
                numberOfWeeks = MenuUtilities.GetSimpleInput(new IntRangeInputValidator(1, 52));
            }
            else
            {
                numberOfWeeks = 1;
            }

            Reservations reservations = ReservationFactory.createReservations(since, to, numberOfWeeks, obj, PickAction.NOT_PICKED);
            return service.addReservation(reservations);

        }

    }
}
